package sweep;

import java.awt.Frame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Panel;
import java.math.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.net.Socket;
import java.io.IOException;
import java.io.DataOutputStream;
import java.util.concurrent.TimeUnit;

class Block {
    private int x;
	private int y;
	private boolean bomb;
	private boolean flag;
	private boolean unsure;
	private boolean show;
    public int near_bombs;

    public void reset_near(int m_bombs) {near_bombs = m_bombs;}

    public Block(boolean m_bomb, int m_x, int m_y) {
        bomb = m_bomb;
	    x = m_x;
	    y = m_y;
	    flag = unsure = show = false;
    }
	public void SetFlag() {
        if (flag == false && unsure == false) {
            flag = true;
        }
        else if (flag == true && unsure == false) {
            flag = false;
            unsure = true;
        }
        else if (unsure = true) {
            unsure = false;
        }
    }
	public void PlaceBomb() { bomb = true; }
	public void draw(int length, int width, Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.DARK_GRAY);
        // g2.setBackground(Color.LIGHT_GRAY);
        g2.fillRect(x + 2, y + 2, length - 4, width - 4);
        if (show == true) {
            g2.setColor(Color.LIGHT_GRAY);
            g2.fillRect(x + 2, y + 2, length - 4, width - 4);
            if (bomb == true) {
                g2.setColor(Color.RED);
                g2.fillOval(x + length / 2 - 10, y + width / 2 - 10, length / 8 * 4, length / 8 * 4);
            }
            else if (near_bombs == 1) {
                g2.setColor(Color.BLUE);                
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("1", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (near_bombs == 2) {
                g2.setColor(Color.YELLOW);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("2", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (near_bombs == 3) {
                g2.setColor(Color.RED);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("3", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (near_bombs == 4) {
                g2.setColor(Color.ORANGE);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("4", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (near_bombs == 5) {
                g2.setColor(Color.CYAN);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("5", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (near_bombs == 6) {
                g2.setColor(Color.MAGENTA);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("6", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (near_bombs == 7) {
                g2.setColor(Color.BLACK);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("7", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (near_bombs == 8) {
                g2.setColor(Color.WHITE);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("8", x + length / 25 * 11, y + width / 10 + 25);
            }
        }
        else {
            if (flag == true) {
                g2.setColor(Color.PINK);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("T", x + length / 25 * 11, y + width / 10 + 25);
            }
            else if (unsure == true) {
                g2.setColor(Color.WHITE);
                g2.setFont(new Font("仿宋",Font.BOLD,30));
                g2.drawString("?", x + length / 25 * 11, y + width / 10 + 25);
            }
        }
    }
	public void SetShow() { 
		show = true; 
		if (flag == true) {
			flag = false;
		}
		else if (unsure == true) {
			unsure = false;
		}
	}
	public void SetUnsure() { unsure = true; }
	public void reset() { show = false; bomb = false; flag = false; unsure = false; }
	public boolean IsBomb() { return bomb; }
	public boolean IsFlag() { return flag; }
	public boolean IsUnsure() { return unsure; }
	public boolean IsShow() { return show; }
	public int GetX() { return x; }
	public int GetY() { return y; }
};

public class Game extends JComponent {
    private int number;
	private int bombs;
	private int flags;
	private Block[][] my_blocks = new Block[9][9];
	private boolean end;
    public Graphics2D g;
    Socket client;
    String rival;

    public Game(Socket client, String rival) {
        this.client = client;
        this.rival = rival;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                my_blocks[i][j] = new Block(false, 140 + j * 40, 60 + i * 40);
            }
        }
        bombs = flags = 10;
        end = false;
        number = 81 - bombs;
        SetBomb();
        // play();
    }
	public void SetBomb() {
        // (int)(Math.random() * 10)
        int number = 0;
	    int[][] known = new int[9][9];
	    int i, j;
	    for (i = 0; i < 9; i++) {
		    for (j = 0; j < 9; j++) {
			    known[i][j] = 0;
		    }
	    }
	    while (number < bombs) {
		    i = (int)(Math.random() * 9);
		    j = (int)(Math.random() * 9);
		    if (known[i][j] == 0) {
			    known[i][j] = 1;
			    my_blocks[i][j].PlaceBomb();
			    number++;
		    }
	    }
    }
	@Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        this.g = g2;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (my_blocks[i][j].IsShow() == true) {
                    int tmp_x = my_blocks[i][j].GetX();
                    int tmp_y = my_blocks[i][j].GetY();
                    my_blocks[i][j].draw(40, 40, g2);
                }
                else {
                    my_blocks[i][j].draw(40, 40, g2);
                }
            }
        }
    }
	public void link(int i, int j) {
        if (FindBombs(i, j) == 0) {
            if (i - 1 >= 0 && j - 1 >= 0 && my_blocks[i - 1][j - 1].IsShow() == false) {
                if (my_blocks[i - 1][j - 1].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i - 1][j - 1].SetShow();
                my_blocks[i - 1][j - 1].reset_near(FindBombs(i - 1, j - 1));
                number--;
                link(i - 1, j - 1);
            }
            if (i - 1 >= 0 && my_blocks[i - 1][j].IsShow() == false) {
                if (my_blocks[i - 1][j].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i - 1][j].SetShow();
                my_blocks[i - 1][j].reset_near(FindBombs(i - 1, j));
                number--;
                link(i - 1, j);
            }
            if (i - 1 >= 0 && j + 1 < 9 && my_blocks[i - 1][j + 1].IsShow() == false) {
                if (my_blocks[i - 1][j + 1].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i - 1][j + 1].SetShow();
                my_blocks[i - 1][j + 1].reset_near(FindBombs(i - 1, j + 1));
                number--;
                link(i - 1, j + 1);
            }
            if (j - 1 >= 0 && my_blocks[i][j - 1].IsShow() == false) {
                if (my_blocks[i][j - 1].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i][j - 1].SetShow();
                my_blocks[i][j - 1].reset_near(FindBombs(i, j - 1));
                number--;
                link(i, j - 1);
            }
            if (j + 1 < 9 && my_blocks[i][j + 1].IsShow() == false) {
                if (my_blocks[i][j + 1].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i][j + 1].SetShow();
                my_blocks[i][j + 1].reset_near(FindBombs(i, j + 1));
                number--;
                link(i, j + 1);
            }
            if (i + 1 < 9 && j - 1 >= 0 && my_blocks[i + 1][j - 1].IsShow() == false) {
                if (my_blocks[i + 1][j - 1].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i + 1][j - 1].SetShow();
                my_blocks[i + 1][j - 1].reset_near(FindBombs(i + 1, j - 1));
                number--;
                link(i + 1, j - 1);
            }
            if (i + 1 < 9 && my_blocks[i + 1][j].IsShow() == false) {
                if (my_blocks[i + 1][j].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i + 1][j].SetShow();
                my_blocks[i + 1][j].reset_near(FindBombs(i + 1, j));
                number--;
                link(i + 1, j);
            }
            if (i + 1 < 9 && j + 1 < 9 && my_blocks[i + 1][j + 1].IsShow() == false) {
                if (my_blocks[i + 1][j + 1].IsFlag() == true) {
                    flags++;
                }
                my_blocks[i + 1][j + 1].SetShow();
                my_blocks[i + 1][j + 1].reset_near(FindBombs(i + 1, j + 1));
                number--;
                link(i + 1, j + 1);
            }
        }
        return;
    }
	public boolean IsWin() { return  number == 0; }
	public boolean IsLose() { return end; }
	public Block GetBlock(int m_x, int m_y) { return my_blocks[m_y / 40][m_x / 40]; }
	public int FindBombs(int i, int j) {
        int near_bombs = 0;
        if (i - 1 >= 0 && j - 1 >= 0 && my_blocks[i - 1][j - 1].IsBomb() == true) {
            near_bombs++;
        }
        if (i - 1 >= 0 && my_blocks[i - 1][j].IsBomb() == true) {
            near_bombs++;
        }
        if (i - 1 >= 0 && j + 1 < 9 && my_blocks[i - 1][j + 1].IsBomb() == true) {
            near_bombs++;
        }
        if (j - 1 >= 0 && my_blocks[i][j - 1].IsBomb() == true) {
            near_bombs++;
        }
        if (j + 1 < 9 && my_blocks[i][j + 1].IsBomb() == true) {
            near_bombs++;
        }
        if (i + 1 < 9 && j - 1 >= 0 && my_blocks[i + 1][j - 1].IsBomb() == true) {
            near_bombs++;
        }
        if (i + 1 < 9 && my_blocks[i + 1][j].IsBomb() == true) {
            near_bombs++;
        }
        if (i + 1 < 9 && j + 1 < 9 && my_blocks[i + 1][j + 1].IsBomb() == true) {
            near_bombs++;
        }
        return near_bombs;
    }
    public void change(MouseEvent m) {
        if (end == true) {
            return;
        }
        if (m.getButton() == m.BUTTON1) {
            if ((m.getX() - 7 - 140) / 40 >= 0 && (m.getX() - 7 - 140) / 40 < 9 && (m.getY() - 30 - 60) / 40 >= 0 && (m.getY() - 30 - 60) / 40 < 9) {
                Block tmp = GetBlock(m.getX() - 140 - 7, m.getY() - 60 - 30);
                int i = (tmp.GetY() - 60) / 40;
                int j = (tmp.GetX() - 140) / 40;
                int near_bombs = FindBombs(i, j);
                if (tmp.IsShow() == false) {
                    if (my_blocks[(m.getY() - 60 - 30) / 40][(m.getX() - 140 - 7) / 40].IsFlag() == true) {
                        flags++;
                    }
                    my_blocks[(m.getY() - 60 - 30) / 40][(m.getX() - 140 - 7) / 40].reset_near(near_bombs);
                    my_blocks[(m.getY() - 60 - 30) / 40][(m.getX() - 140 - 7) / 40].SetShow();
                    if (tmp.IsBomb() == true) {
                        end = true;
                        return;
                    }
                    else {
                        number--;
                    }
                    link(i, j);
                }
                else {
                    int near_flags = 0;
                    if (i - 1 >= 0 && j - 1 >= 0 && my_blocks[i - 1][j - 1].IsFlag() == true) {
                        near_flags++;
                    }
                    if (i - 1 >= 0 && my_blocks[i - 1][j].IsFlag() == true) {
                        near_flags++;
                    }
                    if (i - 1 >= 0 && j + 1 < 9 && my_blocks[i - 1][j + 1].IsFlag() == true) {
                        near_flags++;
                    }
                    if (j - 1 >= 0 && my_blocks[i][j - 1].IsFlag() == true) {
                        near_flags++;
                    }
                    if (j + 1 < 9 && my_blocks[i][j + 1].IsFlag() == true) {
                        near_flags++;
                    }
                    if (i + 1 < 9 && j - 1 >= 0 && my_blocks[i + 1][j - 1].IsFlag() == true) {
                        near_flags++;
                    }
                    if (i + 1 < 9 && my_blocks[i + 1][j].IsFlag() == true) {
                        near_flags++;
                    }
                    if (i + 1 < 9 && j + 1 < 9 && my_blocks[i + 1][j + 1].IsFlag() == true) {
                        near_flags++;
                    }
                    if (near_flags == near_bombs) {
                        if (i - 1 >= 0 && j - 1 >= 0 && !my_blocks[i - 1][j - 1].IsShow() && !my_blocks[i - 1][j - 1].IsFlag()) {
                            if (my_blocks[i - 1][j - 1].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i - 1][j - 1].SetShow();
                            my_blocks[i - 1][j - 1].reset_near(FindBombs(i - 1, j - 1));
                            if (my_blocks[i - 1][j - 1].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i - 1, j - 1);
                        }
                        if (i - 1 >= 0 && !my_blocks[i - 1][j].IsShow() && !my_blocks[i - 1][j].IsFlag()) {
                            if (my_blocks[i - 1][j].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i - 1][j].SetShow();
                            my_blocks[i - 1][j].reset_near(FindBombs(i - 1, j));
                            if (my_blocks[i - 1][j].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i - 1, j);
                        }
                        if (i - 1 >= 0 && j + 1 < 9 && !my_blocks[i - 1][j + 1].IsShow() && !my_blocks[i - 1][j + 1].IsFlag()) {
                            if (my_blocks[i - 1][j + 1].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i - 1][j + 1].SetShow();
                            my_blocks[i - 1][j + 1].reset_near(FindBombs(i - 1, j + 1));
                            if (my_blocks[i - 1][j + 1].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i - 1, j + 1);
                        }
                        if (j - 1 >= 0 && !my_blocks[i][j - 1].IsShow() && !my_blocks[i][j - 1].IsFlag()) {
                            if (my_blocks[i][j - 1].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i][j - 1].SetShow();
                            my_blocks[i][j - 1].reset_near(FindBombs(i, j - 1));
                            if (my_blocks[i][j - 1].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i, j - 1);
                        }
                        if (j + 1 < 9 && !my_blocks[i][j + 1].IsShow() && !my_blocks[i][j + 1].IsFlag()) {
                            if (my_blocks[i][j + 1].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i][j + 1].SetShow();
                            my_blocks[i][j + 1].reset_near(FindBombs(i, j + 1));
                            if (my_blocks[i][j + 1].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i, j + 1);
                        }
                        if (i + 1 < 9 && j - 1 >= 0 && !my_blocks[i + 1][j - 1].IsShow() && !my_blocks[i + 1][j - 1].IsFlag()) {
                            if (my_blocks[i + 1][j + 1].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i + 1][j - 1].SetShow();
                            my_blocks[i + 1][j - 1].reset_near(FindBombs(i + 1, j - 1));
                            if (my_blocks[i + 1][j - 1].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i + 1, j - 1);
                        }
                        if (i + 1 < 9 && !my_blocks[i + 1][j].IsShow() && !my_blocks[i + 1][j].IsFlag()) {
                            if (my_blocks[i + 1][j].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i + 1][j].SetShow();
                            my_blocks[i + 1][j].reset_near(FindBombs(i + 1, j));
                            if (my_blocks[i + 1][j].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i + 1, j);
                        }
                        if (i + 1 < 9 && j + 1 < 9 && !my_blocks[i + 1][j + 1].IsShow() && !my_blocks[i + 1][j + 1].IsFlag()) {
                            if (my_blocks[i + 1][j + 1].IsFlag() == true) {
                                flags++;
                            }
                            my_blocks[i + 1][j + 1].SetShow();
                            my_blocks[i + 1][j + 1].reset_near(FindBombs(i + 1, j + 1));
                            if (my_blocks[i + 1][j + 1].IsBomb() == true) {
                                end = true;
                                return;
                            }
                            number--;
                            link(i + 1, j + 1);
                        }
                    }
                    else {
                        if (i - 1 >= 0 && j - 1 >= 0 && !my_blocks[i - 1][j - 1].IsShow() && !my_blocks[i - 1][j - 1].IsFlag()) {
                            my_blocks[i - 1][j - 1].SetUnsure();
                        }
                        if (i - 1 >= 0 && !my_blocks[i - 1][j].IsShow() && !my_blocks[i - 1][j].IsFlag()) {
                            my_blocks[i - 1][j].SetUnsure();
                        }
                        if (i - 1 >= 0 && j + 1 < 9 && !my_blocks[i - 1][j + 1].IsShow() && !my_blocks[i - 1][j + 1].IsFlag()) {
                            my_blocks[i - 1][j + 1].SetUnsure();
                        }
                        if (j - 1 >= 0 && !my_blocks[i][j - 1].IsShow() && !my_blocks[i][j - 1].IsFlag()) {
                            my_blocks[i][j - 1].SetUnsure();
                        }
                        if (j + 1 < 9 && !my_blocks[i][j + 1].IsShow() && !my_blocks[i][j + 1].IsFlag()) {
                            my_blocks[i][j + 1].SetUnsure();
                        }
                        if (i + 1 < 9 && j - 1 >= 0 && !my_blocks[i + 1][j - 1].IsShow() && !my_blocks[i + 1][j - 1].IsFlag()) {
                            my_blocks[i + 1][j - 1].SetUnsure();
                        }
                        if (i + 1 < 9 && !my_blocks[i + 1][j].IsShow() && !my_blocks[i + 1][j].IsFlag()) {
                            my_blocks[i + 1][j].SetUnsure();
                        }
                        if (i + 1 < 9 && j + 1 < 9 && !my_blocks[i + 1][j + 1].IsShow() && !my_blocks[i + 1][j + 1].IsFlag()) {
                            my_blocks[i + 1][j + 1].SetUnsure();
                        }
                    }
                }
            }
        }
        else if (m.getButton() == m.BUTTON3) {
            if ((m.getX() - 140 - 7) / 40 >= 0 && (m.getX() - 7 - 140) / 40 < 9 && (m.getY() - 60 - 30) / 40 >= 0 && (m.getY() - 60 - 30) / 40 < 9) {
                Block tmp = GetBlock(m.getX() - 140 - 7, m.getY() - 60 - 30);
                if (tmp.IsShow() == true) {
                    
                }
                else if (flags > 0 && tmp.IsFlag() == false && tmp.IsUnsure() == false) {
                    my_blocks[(m.getY() - 60 - 30) / 40][(m.getX() - 140 - 7) / 40].SetFlag();
                    flags--;
                }
                else if (tmp.IsFlag() == true) {
                    my_blocks[(m.getY() - 60 - 30) / 40][(m.getX() - 140 - 7) / 40].SetFlag();
                    flags++;
                }
                else if (tmp.IsUnsure() == true) {
                    my_blocks[(m.getY() - 60 - 30) / 40][(m.getX() - 140 - 7) / 40].SetFlag();
                }
            }
        }
    }
}

class Game_tmp extends JFrame {
    private final Game game;
    private Timer timer;
    public Game_tmp(Socket client, String rival) {
        game = new Game(client, rival);
        addMouseListener(new DrawListener(game, this));
        timer = new Timer(100, new TimerHandler(client));
        timer.start();
        add(game);
        pack();
    }
    @Override
    public void paint(Graphics g){
        super.paint(g);
        this.getContentPane().setBackground(Color.white);
    }

    // 处理 Server 消息
    class TimerHandler implements ActionListener {

        Socket client;

        public TimerHandler(Socket client) {this.client = client;}

        @Override
        public void actionPerformed(java.awt.event.ActionEvent e) {
            try {
                String msg = Client.getMessage();
                String[] message = msg.split(" ");
                // 因对方提前完成游戏而失败
                if (message[0].equals("fail") == true) {
                    JOptionPane.showMessageDialog(null,
                    "You lost the race because your rival is faster.",
                    "loser", JOptionPane.INFORMATION_MESSAGE);
                    Sweep.startMenuBoard(client);
                    dispose();
                }
                else if (message[0].equals("success") == true) {
                    JOptionPane.showMessageDialog(null,
                        "You won the race because your rival failed to finish the game.",
                        "winner", JOptionPane.INFORMATION_MESSAGE);
                    Sweep.startMenuBoard(client);
                    dispose();
                }
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
    }
}

class DrawListener implements MouseListener {

    private Game my_game;
    Game_tmp p;

    public DrawListener(Game m_game, Game_tmp tmp) {
        my_game = m_game;
        p = tmp;
    }

    @Override
    public void mousePressed(MouseEvent e) {
        my_game.change(e);
        my_game.repaint();
        DataOutputStream out;
        if (my_game.IsWin() == true) {
            try {
                out = new DataOutputStream(my_game.client.getOutputStream());
                out.writeUTF("game_success " + my_game.rival);
            } catch (IOException ee) {
                ee.printStackTrace();
                return;
            }
            JOptionPane.showMessageDialog(null,
                        "You win!",
                        "winner", JOptionPane.INFORMATION_MESSAGE);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
            Sweep.startMenuBoard(my_game.client);
            p.dispose();

        }
        if (my_game.IsLose() == true) {
            try {
                out = new DataOutputStream(my_game.client.getOutputStream());
                out.writeUTF("game_fail " + my_game.rival);
                System.out.println("game_fail " + my_game.rival);
            } catch (IOException ee) {
                ee.printStackTrace();
                return;
            }
            JOptionPane.showMessageDialog(null,
                        "You touch the bomb!",
                        "loser", JOptionPane.INFORMATION_MESSAGE);
            Sweep.startMenuBoard(my_game.client);
            p.dispose();
        }
    }
    @Override
    public void mouseClicked(MouseEvent e) {}
    @Override
    public void mouseReleased(MouseEvent e) {}   
    @Override
    public void mouseEntered(MouseEvent m) {}
    @Override
    public void mouseExited(MouseEvent m) {}
}

